unit smExceptions;

interface

uses
  smUtils, SysUtils, Gen2, Lua;

type
  /// <summary> Base Exception class. Writes logs on exceptions raised </summary>
  EGameException = class(Exception)
  public
    constructor Create(const Message: String = ''); reintroduce;
    constructor CreateFmt(const Message: String;
      const Args: array of const); reintroduce;
  end;

  // Engine messages
  EEngineInitialize = class(EGameException);

  // TGameList messages
  EGameListRange = class(EGameException)
    constructor Create(const Index, Count: Integer); reintroduce;
  end;
  EGameListNotFound = class(EGameException);
  EGameListBadReleaseProc = class(EGameException);

  // GUI messages
  EGUIInitialize = class(EGameException);
  EGUIModalFailed = class(EGameException);
  EGUILoadingGeneral = class(EGameException);
  EGUILoadingParamFailed = class(EGameException)
    constructor Create(const Field, Value: String); reintroduce;
  end;

  // SceneManager messages
  EScenesNotFound = class(EGameException)
    constructor Create(const Name: AnsiString); reintroduce;
  end;

  // Player messages
  EPlayerInitialize = class(EGameException);
  EPlayerFileNotFound = class(EGameException);
  EPlayerFileReadError = class(EGameException);

  // World messages
  EWorldFileNotFound = class(EGameException);
  EWorldNoLocations = class(EGameException);
  EWorldLocationMissing = class(EGameException);
  EWorldLocationLoad = class(EGameException);

  /// <summary> Lua exception differs from other classes: it takes
  /// message from stack of Lua machine </summary>
  ELuaException = class(Exception)
  public
    constructor Create(const Lua: lua_State; const Message: String = '');
      reintroduce;
    constructor CreateFmt(const Lua: lua_State; const Message: String;
      const Args: array of const); reintroduce;
  end;
  ELuaInitialize = class(ELuaException);
  ELuaInitfile = class(ELuaException);
  ELuaInitfileStop = class(ELuaException);
  ELuaArgumentCheck = class(ELuaException);

implementation

{ EGameException }

constructor EGameException.Create;
begin
  inherited Create(Message);

  G2WriteLogTimed(AnsiString(Format(
    'An error occured (class %s): %s!',
    [ Self.ClassName, Message ]
  )));
end;

constructor EGameException.CreateFmt;
begin
  Self.Create(Format(Message, Args));
end;

{ EGameListRange }

constructor EGameListRange.Create(const Index, Count: Integer);
begin
  inherited CreateFmt(
    'error accessing list element (index: %d, count: %d)',
    [ Index, Count ]
  );
end;

{ EGUILoadingParamFailed }

constructor EGUILoadingParamFailed.Create(const Field, Value: String);
begin
  inherited CreateFmt(
    'unknown field or value (field: %s, value: "%s")',
    [ Field, Value ]
  );
end;

{ EScenesNotFound }

constructor EScenesNotFound.Create(const Name: AnsiString);
begin
  inherited CreateFmt('scene "%s" not found', [ Name ]);
end;

{ ELuaException }

constructor ELuaException.Create(const Lua: lua_State; const Message: String);
var
  LuaMsg: AnsiString;
begin
  if lua_isstring(Lua, -1) then
  begin
    LuaMsg :=
      AnsiString(Message) + #13#10#9 + AnsiString(lua_tostring(Lua, -1));
    lua_remove(Lua, -1);
  end
  else
    LuaMsg := AnsiString(Message);

  G2WriteLogTimed(AnsiString(Format(
    'A script error occured (class %s): %s!', [Self.ClassName, String(LuaMsg)])
  ));
  inherited Create(String(LuaMsg));
end;

constructor ELuaException.CreateFmt(const Lua: lua_State; const Message: String;
  const Args: array of const);
begin
  Self.Create(Lua, Format(Message, Args));
end;

end.
